package com.tebiecloud.uaa.admin.server.configuration;

import com.tebiecloud.common.handler.CustomAccessDeniedHandler;
import com.tebiecloud.common.handler.CustomAuthenticationEntryPoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * oauth2资源服务器配置
 * @Author: tebie
 * @Date: 2019-08-10 09:08
 */
@Slf4j
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    private BearerTokenExtractor tokenExtractor = new BearerTokenExtractor();

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 配置session的创建策略，默认IF_REQUIRED：Spring Security只会在需要时创建一个HttpSession
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                // 地址认证规则配置
                .and()
                .authorizeRequests()
                .antMatchers("/login/**","/oauth/**").permitAll() // 指定放行地址
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() // 监控端点内部放行
                .anyRequest().authenticated() // 未指定地址均需认证

                // 登录配置放行
                .and()
                .formLogin().loginPage("/login").permitAll()

                // 登出配置与放行
                .and()
                .logout()// .permitAll()
                .addLogoutHandler(new CookieClearingLogoutHandler("token", "remember-me")) // 清除cookie
                .logoutSuccessHandler(new LogoutSuccessHandler()) // 登出成功回调

                // 认证鉴权错误处理，为了统一异常处理，每个资源服务器都应该加上
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new CustomAccessDeniedHandler()) // 自定义访问拒绝处理类，用于解决认证过的用户访问无权限资源时的异常
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) // 自定义未认证处理，用于解决匿名用户访问无权限资源时的异常

                // 禁用csrf
                .and()
                .csrf().disable()

                // 禁用httpBasic
                .httpBasic().disable();
    }

    /**
     * 登出成功回调
     */
    public class LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
        public LogoutSuccessHandler() {
            // 重定向到原地址
            this.setUseReferer(true);
        }

        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            try {
                // 解密请求头
                authentication =  tokenExtractor.extract(request);
                if(authentication!=null && authentication.getPrincipal()!=null){
                    String tokenValue = authentication.getPrincipal().toString();
                    log.debug("revokeToken tokenValue:{}",tokenValue);
                    // 移除token
                    tokenStore.removeAccessToken(tokenStore.readAccessToken(tokenValue));
                }
            }catch (Exception e){
                log.error("revokeToken error:{}",e);
            }
            super.onLogoutSuccess(request, response, authentication);
        }
    }

}
